package ltu.m7011e.client.presenter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import ltu.m7011e.client.LinkServiceRequester;
import ltu.m7011e.client.XMLParser.Parser;
import ltu.m7011e.client.event.PostLinkEvent;
import ltu.m7011e.client.view.MainView;
import ltu.m7011e.shared.SharedConstants;
import ltu.m7011e.shared.model.Author;
import ltu.m7011e.shared.model.Link;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.logical.shared.HasSelectionHandlers;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.XMLParser;
import com.google.gwt.xml.client.impl.DOMParseException;

public class MainPresenter implements Presenter {  

	public interface Display {
		HasClickHandlers getPostTopButton();
		HasClickHandlers getPostNewestButton();
		HasClickHandlers getPostCategoryButton();
		Button getLoginButton();
		Label getUserInfo(); 
		FlexTable getTopLinkTable();
		FlexTable getNewestLinkTable();
		FlexTable getCategoryLinkTable();
		HasSelectionHandlers<Integer> getMainPanel();
		ArrayList<HasClickHandlers> getRadioButton();
		void setData(List<String> data);
		Widget asWidget();
	}

	private final Integer NB_LINK_TO_FETCH=10;
	private final String CATEGORY_TABLE="Category";
	private final String TOP_TABLE="Top";
	private final String NEWEST_TABLE="Newest";
	

	private HashMap<String, ArrayList<Link>> mapCatLink;
	private String categoryToShow;
	private ArrayList<Link> topLinkList;
	private ArrayList<Link> newestLinkList;
	private Author userInfo;


	/**
	 * An instance of the Shared Constants.
	 */
	private final SharedConstants constants = GWT.create(SharedConstants.class);

	private final HandlerManager eventBus;
	private final Display display;
	private final ArrayList<Request> currentRequest;

	public MainPresenter(HandlerManager eventBus, Display view) {
		this.eventBus = eventBus;
		this.display = view;
		this.currentRequest = new ArrayList<Request>();
		this.topLinkList = new ArrayList<Link>();
		this.newestLinkList = new ArrayList<Link>();
		this.mapCatLink = new HashMap<String, ArrayList<Link>>();
		String[] categoryAvailable = constants.cLinkAvailableCategories();
		for (String category : categoryAvailable) {
			mapCatLink.put(category, new ArrayList<Link>());
		}
		this.categoryToShow = categoryAvailable[0];
		this.userInfo = new Author();
	}

	public void bind() {
		display.getPostTopButton().addClickHandler(new ClickHandler() {   
			public void onClick(ClickEvent event) {
				GWT.log("[EVENT] Fire - PostLinkEvent !");
				eventBus.fireEvent(new PostLinkEvent());
			}
		}); 
		display.getPostCategoryButton().addClickHandler(new ClickHandler() {   
			public void onClick(ClickEvent event) {
				GWT.log("[EVENT] Fire - PostLinkEvent !");
				eventBus.fireEvent(new PostLinkEvent());
			}
		});
		display.getPostNewestButton().addClickHandler(new ClickHandler() {   
			public void onClick(ClickEvent event) {
				GWT.log("[EVENT] Fire - PostLinkEvent !");
				eventBus.fireEvent(new PostLinkEvent());
			}
		});

		display.getLoginButton().addClickHandler(new ClickHandler() {   
			public void onClick(ClickEvent event) {
				GWT.log("[getLoginButton] Fire - Login !");
//				if ( userInfo.isLoggedIn()){
//					Window.Location.assign(userInfo.getLoginUrl());
//				}else {
//					Window.Location.assign(userInfo.getLogoutUrl());
//				}
			}
		});

		display.getMainPanel().addSelectionHandler(new SelectionHandler<Integer>() {		
			@Override
			public void onSelection(SelectionEvent<Integer> event) {
				if ( event.getSelectedItem().equals(MainView.POSITION_OF_TOPLINK_TAB) ){
					GWT.log("Call to  fetchNewestLinks !");
					topLinkList.clear();
					fetchTopLinks(NB_LINK_TO_FETCH,0);
				}else if ( event.getSelectedItem().equals(MainView.POSITION_OF_NEWESTLINK_TAB) ) {
					GWT.log("Call to  fetchNewestLinks !");
					newestLinkList.clear();
					fetchNewestLinks(NB_LINK_TO_FETCH,0);
				}else if ( event.getSelectedItem().equals(MainView.POSITION_OF_CATEGORY_TAB) ) {
					GWT.log("Call to  fetchLinksCategory !");
					displayCategory(categoryToShow, 0); 
				}
			}
		});

		ArrayList<HasClickHandlers> myRadioBoxHandler = display.getRadioButton();
		for (HasClickHandlers hasClickHandlers : myRadioBoxHandler) {
			hasClickHandlers.addClickHandler(new ClickHandler() {	
				@Override
				public void onClick(ClickEvent event) {		
					RadioButton evenSource = (RadioButton) event.getSource();
					GWT.log("[myCheckBoxHandler] ClickEvent ="+evenSource.getText()+" clicked");
					categoryToShow=evenSource.getText();
					displayCategory(evenSource.getText(),0);
				}
			});
		}

		// TODO : Implement a graphic element for number of links displayed ?
		fetchTopLinks(NB_LINK_TO_FETCH,0);
		fetchLoginInfo();
	}
	
	private void refreshHeader (){
		GWT.log("[refreshHeader] Start");
		if ( userInfo.isLoggedIn() ) {
			GWT.log("[refreshHeader] User is logged in");
			Label userInfoLabel = this.display.getUserInfo();
			userInfoLabel.setText(userInfo.getNickname());
			this.display.getLoginButton().setHTML("<a href=\""+userInfo.getLogoutUrl()+"\">Logout</a>");
		}else {
			GWT.log("[refreshHeader] User not logged in");
			this.display.getLoginButton().setHTML("<a href=\""+userInfo.getLoginUrl()+"\">Login using google account</a>");
		}

	}

	private void displayCategory(String categoryName, Integer firstLinkToFetch){
		GWT.log("[displayCategory] Start");

		// If the "LinkList" corresponding to the category to load is empty
		if ( mapCatLink.get(categoryName).isEmpty() ) {
			// We fetch the links from the server
			fetchLinksCategory(categoryName, firstLinkToFetch, NB_LINK_TO_FETCH);
		}else{
			buildCategoryLinkTable(categoryName);
		}

	}

	private void fetchLoginInfo(){
		GWT.log("[fetchLoginInfo] Start");
		String url = constants.cAPILoginInfoURL();
		RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
		try {
			// Send the POST Request
			Request postLinkRequest = builder.sendRequest(null, new RequestCallback() {	
				public void onResponseReceived(Request request, Response response) {
					if (200 == response.getStatusCode()) {
						GWT.log("[fetchLoginInfo] - Server Sucess : "+response.getText());
						currentRequest.remove(currentRequest.indexOf(request));
						prossessXmlLinkResponse(response);
					} else {
						GWT.log("[fetchLoginInfo] - Server ERROR != 200 ");
					}
				}
				public void onError(Request request, Throwable exception) {
					GWT.log("[fetchLoginInfo] - Server FAIL : "+exception.getMessage());
					Window.alert("Impossible to load login information");
				}
			});
			currentRequest.add(postLinkRequest);
		} catch (RequestException e) {
			GWT.log("[fetchLoginInfo][ERROR] RequestException :"+e.getMessage());
			Window.alert("An exception have been thrown - Impossible to load login information ...");
		}

	}


	private void fetchTopLinks (Integer numberOfLink, Integer firstResult){
		GWT.log("[fetchTopLinks] Start");
		String url = constants.cAPIGetTopLinkURL()+numberOfLink.toString()+
				constants.cAPIGetLinkFirstResultParam()+firstResult.toString();
		RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
		try {
			// Send the POST Request
			Request postLinkRequest = builder.sendRequest(null, new RequestCallback() {	
				public void onResponseReceived(Request request, Response response) {
					if (200 == response.getStatusCode()) {
						GWT.log("[fetchTopLinks] - Server Sucess : "+response.getText());
						currentRequest.remove(currentRequest.indexOf(request));
						prossessXmlLinkResponse(response);
					} else {
						GWT.log("[fetchTopLinks] - Server ERROR != 200 ");
					}
				}

				public void onError(Request request, Throwable exception) {
					GWT.log("[fetchTopLinks] - Server FAIL : "+exception.getMessage());
					Window.alert("Impossible to load the newest Links");
				}
			});
			currentRequest.add(postLinkRequest);
		} catch (RequestException e) {
			GWT.log("[fetchTopLinks][ERROR] RequestException :"+e.getMessage());
			Window.alert("An exception have been thrown - Impossible to load the newest Links ...");
		}

	}

	private void fetchNewestLinks(Integer numberOfLink, Integer firstResult){
		GWT.log("[fetchNewestLinks] Start");
		String url = constants.cAPIGetNewestLinkURL()+numberOfLink.toString()+
				constants.cAPIGetLinkFirstResultParam()+firstResult.toString();
		RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
		try {
			// Send the POST Request
			Request postLinkRequest = builder.sendRequest(null, new RequestCallback() {	
				public void onResponseReceived(Request request, Response response) {
					if (200 == response.getStatusCode()) {
						GWT.log("[fetchNewestLinks] - Server Sucess : "+response.getText());
						currentRequest.remove(currentRequest.indexOf(request));
						prossessXmlLinkResponse(response);
					} else {
						GWT.log("[fetchNewestLinks] - Server ERROR != 200 ");
					}
				}

				public void onError(Request request, Throwable exception) {
					GWT.log("[fetchNewestLinks] - Server FAIL : "+exception.getMessage());
					Window.alert("Impossible to load the newest Links");
				}
			});
			currentRequest.add(postLinkRequest);
		} catch (RequestException e) {
			GWT.log("[fetchNewestLinks][ERROR] RequestException :"+e.getMessage());
			Window.alert("An exception have been thrown - Impossible to load the newest Links ...");
		}
	}

	private void fetchLinksCategory (String category, Integer firstResult, Integer numberOfLink){
		GWT.log("[fetchLinksCategory] Start");
		// Define the HTTP Request 
		String url = constants.cAPIGetLinkURL()+numberOfLink.toString()+
				constants.cAPIGetLinkFirstResultParam()+firstResult.toString()+
				constants.cAPIGetLinkCategoryParam()+category;
		RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
		GWT.log("URL : "+ url);
		try {
			// Send the POST Request
			Request postLinkRequest = builder.sendRequest(null, new RequestCallback() {	
				public void onResponseReceived(Request request, Response response) {
					if (200 == response.getStatusCode()) {
						GWT.log("[fetchLinksCategory] - Server Success : "+response.getText());
						currentRequest.remove(currentRequest.indexOf(request));
						prossessXmlLinkResponse(response);
					} else {
						GWT.log("[fetchLinksCategory] - Server ERROR != 200 ");
					}
				}

				public void onError(Request request, Throwable exception) {
					GWT.log("[fetchLinksCategory] - Server FAIL : "+exception.getMessage());
					Window.alert("Impossible to load the newest Links");
				}
			});
			currentRequest.add(postLinkRequest);
		} catch (RequestException e) {
			GWT.log("[fetchLinksCategory][ERROR] RequestException :"+e.getMessage());
			Window.alert("An exception have been thrown - Impossible to load the category Links ...");
		}

	}

	/**
	 * Parse the server response specified as parameter and add the link to the 
	 * internal structure of the presenter ( <code>mapCatLink</code> or <code>newestLinkList</code> <code>topLinkList</code> )
	 * depending of the response action name 
	 * @param response to parse
	 */
	private void prossessXmlLinkResponse(Response response){

		if ( ! response.getText().isEmpty() ){
			try {
				Document xmlResponse = XMLParser.parse(response.getText());
				GWT.log("[prossessXmlLinkResponse] XML Paser - Success");
				String actionCategory = Parser.GetResponseAction(xmlResponse);

				if ( actionCategory.startsWith(constants.cXmlGetLinkCatAction()) ){
					String[] availableCategory = constants.cLinkAvailableCategories();
					for (String category : availableCategory) {
						if (actionCategory.contains(category)) {
							mapCatLink.get(category).addAll(Parser.GetLinkArray(xmlResponse));
							buildCategoryLinkTable(category);
						}
					}
				}else if ( actionCategory.equalsIgnoreCase(constants.cXmlGetTopLinkAction()) ){
					GWT.log("[prossessXmlLinkResponse] getTopLinkAction - Detected");
					topLinkList.addAll(Parser.GetLinkArray(xmlResponse));
					buildTopLinkTable();
				}else if ( actionCategory.equalsIgnoreCase(constants.cXmlGetNewestLinkAction()) ){
					GWT.log("[prossessXmlLinkResponse] getNewestLinkAction - Detected");
					newestLinkList.addAll(Parser.GetLinkArray(xmlResponse));
					buildNewestLinkTable();
				}else if ( actionCategory.equalsIgnoreCase(constants.cXmlUserInfoAction()) ){
					GWT.log("[prossessXmlLinkResponse] UserInfoAction - Detected");
					userInfo = Parser.GetUserInfo(xmlResponse);
					if (userInfo != null){
						GWT.log("[refreshHeader] UserInfo != null");
						refreshHeader();
					}else {
						GWT.log("[refreshHeader] ERROR  userInfo= null");
					}
				}

			} catch (DOMParseException e) {
				GWT.log("[ERROR] prossessXmlLinkResponse - Impossible to parse server response !");
				e.printStackTrace();
			}
		}
	}

	private void buildTopLinkTable(){
		GWT.log("[buildTopLinkTable] Start");

		// Fetch the linkTable from the MainView
		FlexTable topLinkTable = this.display.getTopLinkTable();

		// Delete the "... loading ..." tag
		topLinkTable.setHTML(0, 0, "<h2>Top Links :<h2>");
		cleanLinkTable(TOP_TABLE);
		for (Link linkToAdd : topLinkList) {
			addLinkInTable(linkToAdd, topLinkTable);
		}

		GWT.log("[buildTopLinkTable] topLinkList.size = "+topLinkList.size());
		if (topLinkList.size() >= NB_LINK_TO_FETCH) {
			addMoreResultButton(constants.cTopLinkTag(), topLinkTable);
		}
	}

	private void buildNewestLinkTable(){
		GWT.log("[buildNewestLinkTable] Start");

		// Fetch the linkTable from the MainView
		FlexTable newestLinkTable = this.display.getNewestLinkTable();

		// Delete the "... loading ..." tag
		newestLinkTable.setHTML(0, 0, "<h2>Newest Links :<h2>");

		cleanLinkTable(NEWEST_TABLE);
		for (Link linkToAdd : newestLinkList) {
			addLinkInTable(linkToAdd, newestLinkTable);
		}

		GWT.log("[buildNewestLinkTable] newestLinkList.size = "+newestLinkList.size());
		if (topLinkList.size() >= NB_LINK_TO_FETCH) {
			addMoreResultButton(constants.cTopLinkTag(), newestLinkTable);
		}
	}

	private void buildCategoryLinkTable(String category){
		GWT.log("[buildCategoryLinkTable]");

		// Fetch the linkTable from the MainView
		FlexTable categoryflexTable = this.display.getCategoryLinkTable();
		cleanLinkTable(CATEGORY_TABLE);
		for (Link linkToAdd : mapCatLink.get(category)) {
			addLinkInTable(linkToAdd, categoryflexTable);	
		}

		if (mapCatLink.get(category).size() >= NB_LINK_TO_FETCH) {
			addMoreResultButton(category, categoryflexTable);
		}
	}

	private void addMoreResultButton(String category, FlexTable tableToFill){
		tableToFill.setWidget(tableToFill.getRowCount(),0,new Button("<h3> Show more result </h3>",
				new MoreResultHandler(category, tableToFill.getRowCount(), tableToFill )));
	}

	/**
	 * Insert the link in the FlexTable specified as parameter
	 * @param linkToAdd
	 * @param categoryTable 
	 */
	private void addLinkInTable(Link linkToAdd, FlexTable flexTableToFill){

		HorizontalPanel linkPanel = new HorizontalPanel();
		GWT.log("Call to addLinkInTable - processing link : "+linkToAdd.getTheLink());

		linkPanel.add(new HTML("<img src=http://api1.thumbalizr.com/?url="+ linkToAdd.getTheLink()+" alt="+linkToAdd.getDescription()+">"));
		VerticalPanel infoPanel = new VerticalPanel();
		infoPanel.add(new HTML("<a href=\"http://"+linkToAdd.getTheLink()+"\">"+linkToAdd.getDescription()+"</a>"));
		infoPanel.add(new HTML("<b>Category :</b>"+linkToAdd.getLinkCat()));
		infoPanel.add(new HTML("<b>Published by : </b>"+linkToAdd.getLinkAuthor()+" <br/><br/> "+linkToAdd.getPostDate().toString()));
		linkPanel.add(infoPanel);
		flexTableToFill.setWidget(flexTableToFill.getRowCount(), 0, linkPanel);
		flexTableToFill.setWidget(flexTableToFill.getRowCount()-1,1,new Button("<h3>"+linkToAdd.getLikes().toString()+" Likes </h3>",
				new LikeHandler(linkToAdd.getId()) ));
	}

	private void cleanLinkTable(String tableToClean){	
		GWT.log("[cleanCategoryLinkTable] Start");
		// Fetch the linkTable from the MainView
		
		FlexTable flexTableToClean = null;
		
		if ( tableToClean.equalsIgnoreCase(CATEGORY_TABLE)) {
			flexTableToClean = this.display.getCategoryLinkTable();
		}else if ( tableToClean.equalsIgnoreCase(NEWEST_TABLE)) {
			flexTableToClean = this.display.getNewestLinkTable();
		}else if ( tableToClean.equalsIgnoreCase(TOP_TABLE)) {
			flexTableToClean = this.display.getTopLinkTable();
		}
		GWT.log("[cleanCategoryLinkTable] number of row  ="+flexTableToClean.getRowCount());

		// Return if empty table
		if ( flexTableToClean.getRowCount() == 1 ){	
			GWT.log("[cleanCategoryLinkTable]r EMPTY Table  => Return ");
			return;
		}

		for (int i=flexTableToClean.getRowCount() ; i > 1 ; --i) {
			GWT.log("[cleanCategoryLinkTable] bfore "+i+" number of row ="+flexTableToClean.getRowCount());
			flexTableToClean.removeRow(i-1);
			GWT.log("[cleanCategoryLinkTable] deleting ="+flexTableToClean.getRowCount());
		}
	}


	// Create a handler for the sendButton and nameField
	public class LikeHandler implements ClickHandler {
		private Integer linkId;

		public LikeHandler(Integer idLink){
			this.linkId = idLink;
		}

		/**
		 * Fired when the user clicks on the postButton.
		 */
		public void onClick(ClickEvent event) {
			sendLikeToServer(linkId);
		}

		private void sendLikeToServer (Integer linkId){

			String url = constants.cAPILikeLinkIdURL()+linkId.toString();
			RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
			try {
				// Send the GET Request
				Request likeLinkIdRequest = builder.sendRequest(null, new RequestCallback() {	
					public void onResponseReceived(Request request, Response response) {
						if (200 == response.getStatusCode()) {
							GWT.log("[sendLikeToServer] Success : "+response.getStatusText());
							currentRequest.remove(currentRequest.indexOf(request));
							//prossessXmlLinkResponse(response);
						} else if (406 == response.getStatusCode()) {
							GWT.log("[sendLikeToServer] Code 406 - User not Logged-on !");
							Window.alert("You need to be logged to post Likes !");
						}else if (412 == response.getStatusCode()) {
							GWT.log("[sendLikeToServer] Code 406 - User already like !");
							Window.alert("You already like this links !");
						}else {
							GWT.log("[sendLikeToServer] ERROR != 200 ");
						}
					}

					public void onError(Request request, Throwable exception) {
						GWT.log("[sendLikeToServer]  FAIL : "+exception.getMessage());
						Window.alert("Impossible to load the newest Links");
					}
				});
				currentRequest.add(likeLinkIdRequest);
			} catch (RequestException e) {
				GWT.log("[sendLikeToServer] RequestException "+e.getMessage());
				Window.alert("An exception have been thrown - Impossible to load the newest Links ...");
			}
		}// End of sendLikeToServer

	}//LikeHandler


	// Create a handler for the sendButton and nameField
	public class MoreResultHandler implements ClickHandler {
		private String categoryToFetch;
		private Integer buttonPosition;
		private FlexTable tableToTreat;

		public MoreResultHandler(String category, Integer buttonPositionInFlewTable, FlexTable tableToTreat){
			this.categoryToFetch = category;
			this.buttonPosition = buttonPositionInFlewTable;
			this.tableToTreat = tableToTreat;
		}

		/**
		 * Fired when the user clicks on the postButton.
		 */
		public void onClick(ClickEvent event) {
			// We look from where category the button Handler is associated 
			if ( categoryToFetch.equalsIgnoreCase(constants.cNewestLinkTag()) ){
				GWT.log("[MoreResultHandler] fetch NewestLink 10, "+newestLinkList.size());
				fetchNewestLinks(NB_LINK_TO_FETCH, newestLinkList.size());
			}else if ( categoryToFetch.equalsIgnoreCase(constants.cTopLinkTag()) ){
				GWT.log("[MoreResultHandler] fetch TopLinks 10, "+topLinkList.size());
				fetchTopLinks(NB_LINK_TO_FETCH, topLinkList.size());
			}else {
				String[] availableCategory = constants.cLinkAvailableCategories();
				for (String category : availableCategory) {
					if (category.equalsIgnoreCase(categoryToFetch)) {
						GWT.log("[MoreResultHandler] fetch Category : "+ category+ " 10, "+mapCatLink.get(category).size());
						fetchLinksCategory(category, mapCatLink.get(category).size(), NB_LINK_TO_FETCH);
					}
				}
			}
			tableToTreat.removeRow(buttonPosition);
		}// End of OnClick implementation

	}// End of MoreResultHandler

	public void go(final HasWidgets container) {
		bind();
		container.clear();
		container.add(display.asWidget());
	}

}

